package com.shanhy.example.agent;


import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;

import java.lang.instrument.Instrumentation;

/**
 * 代理入口类
 *
 * @author 单红宇
 * @date 2024-03-06 17:22:31
 */
public class PreAgent {

    /**
     * premain
     *
     * @param agentArgs 代理参数
     * @param inst      Instrumentation 是Java提供的一个接口，提供了类定义和类加载相关的服务，
     *                  允许在类加载期间动态地修改类文件字节码，也就是所谓的字节码增强或字节码操作
     */
    public static void premain(String agentArgs, Instrumentation inst) {
        // listener 用来监听扫描和处理的class情况，比如哪些处理了，哪些发生异常了
        // 开发调试阶段 onError 方法很重要，可以方法签名和参数不匹配等错误问题
        AgentBuilder.Listener listener = new AgentBuilder.Listener() {
            @Override
            public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
                // System.out.println("onDiscovery: " + typeName);
            }

            @Override
            public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) {
                // System.out.println("onTransformation: " + typeDescription.getName());
            }

            @Override
            public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded) {
                // System.out.println("onIgnored: " + typeDescription.getName());
            }

            @Override
            public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) {
                System.out.println("onError: " + typeName);
                throwable.printStackTrace();
            }

            @Override
            public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
                // System.out.println("onComplete: " + typeName);
            }

        };

        // transform 指定的转换器来对匹配到的class进行操作
        AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
            return builder
//                    .method(ElementMatchers.named("executeInternal")) // 拦截指定的方法executeInternal
//                    .method(ElementMatchers.any()) // 拦截任意方法
                    .method(ElementMatchers.not(ElementMatchers.isStatic())) // 拦截非静态方法
                    .intercept(MethodDelegation.to(com.shanhy.example.agent.MonitorMethod.class)); // 将拦截到的方法委托给目标类处理
        };

        // type 通过ElementMatcher 来匹配我们加载的class
        new AgentBuilder
                .Default()
//                .type(ElementMatchers.nameStartsWith("com.mysql.cj.jdbc.ClientPreparedStatement"))
                .type(ElementMatchers.nameStartsWith("com.shanhy.example.agent"))
//                .type(ElementMatchers.any())
                .transform(transformer)
//                .with(listener)
                .installOn(inst);

        // 第二种匹配和增强规则
        new AgentBuilder.Default()
                .type(ElementMatchers.nameStartsWith("com.shanhy.example.agent.Hello"))
                .transform((builder, typeDescription, classLoader, module) ->
                        // 所有静态方法并且排除掉静态的构造方法、排除掉void方法、排除到static构造方法
                        builder.visit(Advice.to(MonitorStaticMethod.class).on(ElementMatchers.isStatic()
                                .and(ElementMatchers.not(ElementMatchers.returns(TypeDescription.VOID)))
                                .and(ElementMatchers.not(ElementMatchers.isTypeInitializer()))))
                )
                .with(listener)
                .installOn(inst);

    }

}
